#include <stdint.h>
#include <stdio.h>
// includes the right splitlock version
#include "pwext.hpp"
#include <stdio.h>

pw::semaphore ready(0), sem1(0), sem2(0), resource(1);
bool stop = false;
int steps = 0;


// [[ex03ib]]
// DEBUG descr A worker thread reporting its intent to the split lock.
class worker : public pw::pwthread
{
	friend class debugger;
public:
	// DEBUG {
	worker() :
		split_(true)
	{ }
	struct request {
		int op;
	};
	bool get_next_request_nowait(request *r)
	{
		return false;
	}
	void get_next_request(request *r)
	{
		sem1.wait();
	}
	// DEBUG }
	// ...
	void *execute()
	{
		bool done = false;
		split_.set_worker_intent(
			pw::splitlock::WORKER_WILL_ENTER);
		// ... initialize ...
	// DEBUG {
		ready.signal();
	// DEBUG }
		while(!done)
		{
			// intent is always WILL_ENTER at this point

			request req;
			// get the next request
			if (!get_next_request_nowait(&req))  {
				// no request available immediately, and no
				// way to tell when it will be (depending on
				// the usage, WORKER_MIGHT_ENTER could be a
				// better choice here)
	// DEBUG {
				split_.set_worker_intent(pw::splitlock::WORKER_MIGHT_ENTER);
				if (0)
	// DEBUG }
				split_.set_worker_intent(
					pw::splitlock::WORKER_WONT_ENTER);
				get_next_request(&req);
				// split_.enter() will set it implicitly, but
				// the explicit setting is more reliable
				split_.set_worker_intent(
					pw::splitlock::WORKER_WILL_ENTER);
			}

	// DEBUG {
			printf("worker trying to enter\n");
	// DEBUG }
			split_.enter();
			// ... process the request ...
	// DEBUG {
			printf("worker entered\n");
			++steps;
			done = stop;
	// DEBUG }
			if (done)
				split_.set_worker_intent(
					pw::splitlock::WORKER_WONT_ENTER);
			else
				split_.set_worker_intent(
					pw::splitlock::WORKER_WILL_ENTER);
			split_.leave();
	// DEBUG {
			printf("worker left\n");
	// DEBUG }
		}
		return 0;
	}

protected:
	pw::splitlock split_;
};
// END

class debugger
{
public:

static void ex03ic()
{
int nsteps = 1;
worker *worker_ptr;
int n_workers = 1;
worker *workers[1];

sem1.signal(100);

workers[0] = worker_ptr = new worker;
worker_ptr->start();
ready.wait();

// [[ex03ic]]
// DEBUG descr Single-stepping predictably through a split lock.
for (int i = 0; i < nsteps; i++) {
	// wait limit if the worker's intent is unknown
	struct timespec limit; 
	// the current time
	clock_gettime(CLOCK_REALTIME, &limit); 
	limit.tv_sec++; // wait for 1 second

	worker_ptr->split_.wait_for_worker(limit);
	if (worker_ptr->split_.release_safe_step()) {
		worker_ptr->split_.wait_safe();
		printf("worker made a step\n");
	// DEBUG {
		printf("worker steps total %d\n", steps);
	// DEBUG }
	} else {
		printf("worker is not ready to do the step\n");
		break;
	}
}
// END
// [[ex03id]]
{
	// wait limit if the worker's intent is unknown
	struct timespec limit; 
	// the current time, no timeout
	clock_gettime(CLOCK_REALTIME, &limit); 
	worker_ptr->split_.wait_for_worker(limit);
}
// END

// DEBUG {
stop = true;
for (int i = 0; i < nsteps; i++) {
	struct timespec limit; // wait limit if the worker's intent is unknown
	clock_gettime(CLOCK_REALTIME, &limit); // the current time
	limit.tv_sec++; // wait for 1 second

	worker_ptr->split_.wait_for_worker(limit);
	if (worker_ptr->split_.release_safe_step()) {
		worker_ptr->split_.wait_safe();
		printf("worker made a step\n");
	// DEBUG {
		printf("worker steps total %d\n", steps);
	// DEBUG }
	} else {
		printf("worker is not ready to do the step\n");
		break;
	}
}
// DEBUG }
//
// [[ex03ie]]
// DEBUG descr Waiting until the worker thread intent in the split lock becomes known.
for (int i = 0; i < n_workers; i++)
	workers[i]->split_.request_safe();
for (int i = 0; i < n_workers; i++)
	workers[i]->split_.wait_safe();
{
	// wait limit if the worker's intent is unknown
	struct timespec limit; 
	// the current time, no sleeping
	clock_gettime(CLOCK_REALTIME, &limit); 
	for (int i = 0; i < n_workers; i++)
		workers[i]->split_.wait_for_worker(limit);
}
// END

// DEBUG {
worker_ptr->join();
delete worker_ptr;
// DEBUG }
}
};

// DEBUG {
int main()
{
	debugger::ex03ic();
	return 0;
}
// DEBUG }
/* Sample output:

worker trying to enter
worker entered
worker left
worker trying to enter
worker made a step
worker steps total 1
worker entered
worker left
worker made a step
worker steps total 2

*/
